/**
 * 
 */
package io.dora.invoker;

import io.dora.base.ServiceInvoker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * @author zhaohongbo
 * @param <T>
 * 
 */
public class DazInvoker<T> implements ServiceInvoker<T> {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	private Class<T> clazz;
	
	private Socket socket;
	
	private String serviceKey;
	
	public DazInvoker(Class<T> clazz, String serviceHost, String serviceKey) {
		this.clazz = clazz;
		this.serviceKey = serviceKey;
		try {
			String[] hostport = serviceHost.split(":");
			String host = "";
			int port = 0;
			
			if (hostport.length > 0) {
				host = hostport[0];
			}
			
			if (hostport.length > 1) {
				port = Integer.parseInt(hostport[1]);
			}
			
			socket = new Socket(host, port);
			
		} catch (UnknownHostException e) {
			logger.error("{}", e);
		} catch (IOException e) {
			logger.error("{}", e);
		}
	}
	
	

	@SuppressWarnings("unchecked")
	@Override
	public T invoke() {
		return (T) Proxy.newProxyInstance(
				Thread.currentThread().getContextClassLoader(), 
				new Class[] { clazz },
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
						output.writeUTF(serviceKey + "?" + method.getName());
						output.writeObject(method.getParameterTypes());
						output.writeObject(args);
						
						ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
						Object resultObj = input.readObject();
						
						if (resultObj instanceof Throwable) {
							throw (Throwable) resultObj;
						}
						
						output.close();
						input.close();
						socket.close();

						return resultObj;
					}
				});
	}
	
	

}
